package utfpr.ct.dainf.engcomp.logica;

import java.util.Arrays;
import java.util.Random;

import utfpr.ct.dainf.engcomp.bean.Cromossomo;
import utfpr.ct.dainf.engcomp.bean.Gene;

public class Populacao {
	private int geracao = 0;
	private Cromossomo[] cromossomos;
	private FitnessFunction fitness;
	private double prCr = 0.45;
	private double prMut = 0.05;
	
	public Populacao(Cromossomo[] cromossomos, FitnessFunction fitness){
		this.cromossomos = new Cromossomo[cromossomos.length];
		for(int i=0; i<cromossomos.length; i++){
			this.cromossomos[i] = new Cromossomo(cromossomos[i].getGeneSize());
			this.cromossomos[i] = cromossomos[i];
		}
		this.fitness = fitness;
	}

	public Cromossomo getCromossomoAt(int index){
		return cromossomos[index];
	}
	
	public void evoluir(){
		this.selecao();
		geracao++ ;
	}
	
	private void selecao(){
		double total = 0;
		Cromossomo[] cruzamento = new Cromossomo[cromossomos.length];
		Cromossomo[] retorno = new Cromossomo[cromossomos.length];
		Cromossomo[] totalPopulacao = new Cromossomo[2*cromossomos.length];
		for(Cromossomo cromossomo : cromossomos){
			double soma = this.fitness.getFitness(cromossomo, geracao);
			total = total + soma;
		}
		int inseridos = 0;
		int i=0;
		while( inseridos < cromossomos.length ){
			double soma = this.fitness.getFitness(cromossomos[i], geracao)/total;
			double r = Math.random();
				while( soma < r ){
					i = i + 1;
					soma = soma + this.fitness.getFitness(cromossomos[i], geracao)/total;
				}
				cruzamento[inseridos++] = cromossomos[i];
				i = 0;
		}
		cruzamento = crossover(cruzamento);
		cruzamento = mutacao(cruzamento);
		for(int k=0; k<cruzamento.length; k++){
			totalPopulacao[2*k] = cruzamento[k];
			totalPopulacao[2*k+1] = cromossomos[k];
		}
		Arrays.sort(totalPopulacao);
		for(int k=0; k<cromossomos.length; k++){
			retorno[k] = totalPopulacao[(totalPopulacao.length-1)-k];
		}
		this.cromossomos = retorno;
	}
	
	private Cromossomo[] crossover(Cromossomo[] cromossomos){
		Gene[] temp = new Gene[cromossomos[0].getGeneSize()];
		for(int i=0; i< Math.ceil(cromossomos.length/2); i++){
			int pontoMut = new Random().nextInt(cromossomos[0].getGeneSize()-1);
			double r = Math.random();			
			if( r < prCr){
				for(int j=0; j<pontoMut; j++){
					temp[j] = cromossomos[2*i].getGeneAt(j);
					cromossomos[2*i].setGeneAt(j, cromossomos[2*i+1].getGeneAt(j));
					cromossomos[2*i+1].setGeneAt(j, temp[j]);
				}
			}
		}
		return cromossomos;
	}
	
	private Cromossomo[] mutacao(Cromossomo[] cromossomos){
		for(int i=0; i<cromossomos.length; i++){
			double r = Math.random();
			int posMut = new Random().nextInt(cromossomos[i].getGeneSize());
			if( r < prMut ){
				if( (Integer)cromossomos[i].getGeneAt(posMut).getAlelo() == 1 )
					cromossomos[i].getGeneAt(posMut).setAlelo(0);
				else
					cromossomos[i].getGeneAt(posMut).setAlelo(1);
			}
		}
		return cromossomos;
	}
	
	public int getGeracao(){
		return geracao;
	}
	
	public boolean cromossomoVazio(Cromossomo cromossomo){
		int vazios = 0 ;
		for(Gene gene : cromossomo.getGenes()){
			if( (Integer)gene.getAlelo() == 0 )
				vazios++;
		}
		if( vazios == cromossomo.getGeneSize() )
			return true;
		else
			return false;
	}
	
	public void printPopulacao(){
		System.out.println("Popula��o");
		for(int i=0; i<cromossomos.length; i++){
			for(int j=0; j<cromossomos[i].getGeneSize(); j++){
				System.out.print(cromossomos[i].getGeneAt(j).getAlelo());
			}
			System.out.println("");
		}
	}
	
}
